//go:build linux

package http_v3

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"go2/log"
	"net/http"
	"runtime"
	"syscall"
	"time"

	"golang.org/x/sys/unix"
)

type GoServer struct {
	server   *http.Server
	epoll_fd int
	events   []syscall.EpollEvent
	conn_map map[int32]*conn
	buffio   *bufio.Reader
	is_busy  bool
	pro_id   int
}

func NewServer(server *http.Server, pro_id int) *GoServer {
	c := &GoServer{
		conn_map: make(map[int32]*conn),
		server:   server,
		buffio:   bufio.NewReaderSize(bytes.NewReader([]byte{}), 4096),
		// revents是用来存储epoll_wait函数返回的事件的结构体数组。在这个数组中，
		// 每个结构体表示一个事件（比如可读事件、可写事件等），包含该事件所属的文件描述符和事件类型等信息。
		events: epoll_create_events(runtime.NumCPU() * 1024),
		pro_id: pro_id,
	}

	var err error

	c.epoll_fd, err = epoll_init()
	if err != nil {
		log.Errorf("epoll create err: %v", err)
		return nil
	}
	go c.start()

	return c
}

func (c *GoServer) IsBusy() bool {
	return c.is_busy
}

func (c *GoServer) Push(fd int) {
	epoll_add(c.epoll_fd, int32(fd))
}

func (c *GoServer) get_conn(fd int) (conn *conn) {
	//log.Debugf("get_conn: %d", fd)

	var err error
	var ok bool

	fd32 := int32(fd)

	// Double check after acquiring write lock
	if conn, ok = c.conn_map[fd32]; ok {
		return
	}

	//log.Debugf("add_conn: %d", fd)

	if c.server.TLSConfig != nil {
		// Create a base connection first (keep blocking mode for TLS handshake)
		baseConn := NewConn(fd, nil)
		// Wrap it with TLS
		tlsConn := tls.Server(baseConn, c.server.TLSConfig)
		// Perform the handshake
		if err = tlsConn.Handshake(); err != nil {
			//log.Debugf("TLS handshake failed (fd:%d): %v", fd, err)
			// Cleanup the failed connection
			baseConn.Close()
			// Remove from epoll to prevent further events
			epoll_dell(c.epoll_fd, fd32)
			return nil
		}
		// Set non-blocking mode after successful TLS handshake
		epoll_set_nonblock(int(fd))
		// Store the TLS connection
		conn = NewConn(fd, tlsConn)
		c.conn_map[fd32] = conn
		return
	}

	// For non-TLS connections, set non-blocking mode immediately
	epoll_set_nonblock(int(fd))
	conn = NewConn(fd, nil)
	c.conn_map[fd32] = conn
	return
}

func (c *GoServer) del_conn(fd int32) {
	epoll_dell(c.epoll_fd, fd)

	conn, ok := c.conn_map[fd]
	if !ok {
		return
	}
	delete(c.conn_map, fd)

	//log.Debugf("del conn fd: %d", fd)

	conn.Close()
}

// get_timeout_conn_list 批量检查超时连接
func (c *GoServer) get_timeout_conn_list(now time.Time, timeout time.Duration, toDelete *[]int32) {
	//log.Debugf("get_timeout_conn_list now: %v, timeout: %v", now, timeout)

	for fd, conn := range c.conn_map {
		//log.Debugf("range fd: %d, conn: %v", fd, conn)
		if now.Sub(conn.update_time) > timeout {
			log.Debugf("delete timeout fd: %d, conn: %v", fd, conn)
			*toDelete = append((*toDelete), fd)
		}
	}
}

func (c *GoServer) start() error {

	var nready int
	var err error
	var i int
	var cur_fd32 int32
	var buf [8]byte
	var cur_conn *conn

	//runtime.LockOSThread()         // 锁定当前goroutine到操作系统线程
	//defer runtime.UnlockOSThread() // 确保在结束时解锁

	defer syscall.Close(c.epoll_fd)

	timerFd, err := epoll_add_timer(c.epoll_fd, int64(c.server.IdleTimeout)/2, int64(c.server.IdleTimeout)/2)
	if err != nil {
		return err
	}

	// 预分配删除列表，避免循环中重复分配
	toDeleteBuf := make([]int32, 0, 1024)

	for {
		// epoll_wait函数会阻塞等待I/O事件的发生，一旦有事件发生，就会将相应的事件信息存储到revents数组中，并返回已经发生的事件数目n
		// 程序开发人员可以通过遍历revents数组获取每个事件的详细信息，并做出相应的处理逻辑
		// epoll_wait()函数检测到文件描述符就绪时，它会将这些文件描述符添加到一个双向链表中，每个节点表示一个就绪的文件描述符。程序可以通过遍历链表来获取每
		// 个就绪文件描述符的信息，并进行相应的操作，epoll_wait()函数的时间复杂度为O(n)
		c.is_busy = false
		nready, err = syscall.EpollWait(c.epoll_fd, c.events, -1)
		c.is_busy = true
		if err != nil {
			if err == unix.EINTR {
				runtime.Gosched()
				continue
			}
			//log.Debugf("epoll_wait error: %v", err)
			return err
		}

		// 复用slice，减少内存分配
		toDelete := toDeleteBuf[:0]

		for i = range nready {
			cur_fd32 = c.events[i].Fd
			cur_fd := int(cur_fd32)

			if c.events[i].Events&(syscall.EPOLLHUP|syscall.EPOLLERR|syscall.EPOLLRDHUP) != 0 {
				//log.Debugf("EPOLLHUP|EPOLLERR|EPOLLRDHUP fd: %d", cur_fd)
				toDelete = append(toDelete, cur_fd32)
				continue
			}

			if c.events[i].Events&(syscall.EPOLLIN) == 0 {
				continue
			}

			// 检查是否是定时器事件
			if cur_fd == timerFd {
				syscall.Read(timerFd, buf[:]) // 读取定时器事件，防止重复触发
				c.get_timeout_conn_list(time.Now(), c.server.IdleTimeout, &toDelete)
				continue
			}

			cur_conn = c.get_conn(cur_fd)
			if cur_conn == nil {
				//log.Debugf("failed to get or create connection for fd: %d", cur_fd)
				toDelete = append(toDelete, cur_fd32)
				continue
			}

			//log.Debugf("serve_http: %d", cur_fd)

			c.buffio.Reset(cur_conn)
			r, err := http.ReadRequest(c.buffio)
			if err != nil || r == nil {
				//log.Debugf("read request error (fd:%d): %v", cur_fd, err)
				toDelete = append(toDelete, cur_fd32)
				continue
			}

			addr := cur_conn.RemoteAddr()
			if addr == nil {
				log.Debugf("failed to get remote addr (fd:%d)", cur_fd)
				toDelete = append(toDelete, cur_fd32)
				continue
			}

			r.RemoteAddr = addr.String()

			w := NewResponse(cur_conn, c.pro_id)

			c.server.Handler.ServeHTTP(w, r)

			if w.Header().Get("Connection") != "keep-alive" || w.Header().Get("Content-Length") == "" {
				toDelete = append(toDelete, cur_fd32)
			} else {
				c.conn_map[cur_fd32].update_time = time.Now()
			}
		}

		// 批量删除连接
		if len(toDelete) > 0 {
			for _, time_out_fd := range toDelete {
				c.del_conn(time_out_fd)
			}
		}
	}
}
